home *** CD-ROM | disk | FTP | other *** search
/ Computer Select (Limited Edition) / Computer Select.iso / dobbs / v17n05 / crcman.asc < prev    next >
Encoding:
Text File  |  1992-03-30  |  9.7 KB  |  316 lines

  1. _FILE VERIFICATION USING CRC_
  2. by Mark R. Nelson
  3.  
  4. [LISTING ONE]
  5.  
  6. /************************** Start of CRCMAN.C *************************
  7.  * This program is used to build a list of CRC-32 values for all of files in a 
  8.  * given directory tree. After building file, program can be run later to 
  9.  * verify CRC values, giving assurance of integrity of files. To build CRC file
  10.  * command line is: CRCMAN -b root-dir crc-file-name  
  11.  * To check list of files created, run with: CRCMAN crc-file-name
  12.  * Should work with most 16 and 32-bit compilers under MS-DOS and UNIX. */
  13.  
  14. #include <stdio.h>
  15. #include <stdlib.h>
  16. #include <string.h>
  17. #include <sys/types.h>
  18. #include <sys/stat.h>
  19.  
  20. unsigned long CRCTable[ 256 ];
  21.  
  22. /* To build this program under UNIX, define UNIX either on the command line or 
  23.  * by editing this file. To define it on the command line, program should be 
  24.  * built like this: cc -o crcman -DUNIX crcman.c   Code in this program assumes
  25.  * UNIX compiler is K&R variety; does away with real function prototyping. */
  26. #ifdef UNIX
  27.  
  28. #include <varargs.h>
  29. #ifdef M_XENIX
  30. #include <sys/ndir.h>
  31. #else
  32. #include <sys/dirent.h>
  33. #endif /* M_XENIX */
  34.  
  35. #define SEPARATOR "/"
  36. #define FILENAME_SIZE 81
  37.  
  38. void FatalError();
  39. unsigned long CalculateFileCRC();
  40. void ProcessAllFiles();
  41. void BuildCRCFile();
  42. void CheckFiles();
  43. unsigned long CalculateBufferCRC();
  44. void BuildCRCTable();
  45.  
  46. #else /* not UNIX, must be MSDOS */
  47. /* Most MS-DOS compilers have converged on same names for structures and 
  48.  * functions used when searching directories. Borland C implementations use a
  49.  * variant, requiring macro definitions. Functions work in an identical manner,
  50.  * so actual implementation of code is straightforward. Addition of MS-DOS 
  51.  * definition helps convince Zortech compiler to use same structure and 
  52.  * function names as everyone else. */
  53. #define MSDOS 1
  54. #include <stdarg.h>
  55. #include <dos.h>
  56.  
  57. #define SEPARATOR "\\"
  58. #define FILENAME_SIZE FILENAME_MAX
  59.  
  60. #ifdef __TURBOC__
  61.  
  62. #include <dir.h>
  63. #define FILE_INFO                 struct ffblk
  64. #define FIND_FIRST( n, i )        findfirst( ( n ), ( i ), FA_DIREC )
  65. #define FIND_NEXT( info )         findnext( ( info ) )
  66. #define FILE_NAME( info )         ( ( info ).ff_name )
  67.  
  68. #else
  69.  
  70. #define FILE_INFO                 struct find_t
  71. #define FIND_FIRST( n, i )        _dos_findfirst( (n), _A_SUBDIR, (i) )
  72. #define FIND_NEXT( info )         _dos_findnext( ( info ) )
  73. #define FILE_NAME( info )         ( ( info ).name )
  74.  
  75. #endif
  76.  
  77. void FatalError( char *fmt, ... );
  78. unsigned long CalculateFileCRC( FILE *file );
  79. void ProcessAllFiles( char *path, FILE *crc_file );
  80. void BuildCRCFile( char *input_dir_name, char *crc_file_name );
  81. void CheckFiles( char *crc_file_name );
  82. unsigned long CalculateBufferCRC( unsigned int count, unsigned long crc,
  83.                                   void *buffer );
  84. void BuildCRCTable( void );
  85.  
  86. #endif  /* UNIX */
  87.  
  88. /* Main program checks for valid occurences of two different types of command 
  89.  * lines, and executes them if found. Otherwise, it prints out a simple 
  90.  * usage statement and exits.  */
  91. int main( argc, argv )
  92. int argc;
  93. char *argv[];
  94. {
  95.     setbuf( stdout, NULL );
  96.     BuildCRCTable();
  97.     if ( argc == 2 )
  98.         CheckFiles( argv[ 1 ] );
  99.     else if ( argc == 4 && strcmp( argv[ 1 ], "-b" ) == 0 )
  100.         BuildCRCFile( argv[ 2 ], argv[ 3 ] );
  101.     else {
  102.         printf( "Usage: CRCMAN [-b input_dir] crc-file \n" );
  103.         printf( "\n" );
  104.         printf( "Using the -b option checks all files under the input_dir\n" );
  105.         printf( "and appends their data to the crc-file.  Otherwise, the\n" );
  106.         printf( "program checks the CRC data of all of the files in the\n" );
  107.         printf( "crc-file and prints the results\n" );
  108.         return( 1 );
  109.     }
  110.     return( 0 );
  111. }
  112. /* Instead of performing a straightforward calculation of the 32-bit CRC using 
  113.  * a series of logical operations, program uses faster table lookup method. */
  114. #define CRC32_POLYNOMIAL     0xEDB88320L
  115.  
  116. void BuildCRCTable()
  117. {
  118.     int i;
  119.     int j;
  120.     unsigned long crc;
  121.  
  122.     for ( i = 0; i <= 255 ; i++ ) {
  123.         crc = i;
  124.         for ( j = 8 ; j > 0; j-- ) {
  125.             if ( crc & 1 )
  126.                 crc = ( crc >> 1 ) ^ CRC32_POLYNOMIAL;
  127.             else
  128.                 crc >>= 1;
  129.         }
  130.         CRCTable[ i ] = crc;
  131.     }
  132. }
  133. /* Routine checks CRC values for a list of files. */
  134. void CheckFiles( crc_file_name )
  135. char *crc_file_name;
  136. {
  137.     FILE *crc_file;
  138.     FILE *test_file;
  139.     unsigned long log_crc;
  140.     unsigned long crc;
  141.     char log_name[ FILENAME_SIZE ];
  142.     int result;
  143.  
  144.     crc_file = fopen( crc_file_name, "r" );
  145.     if ( crc_file == NULL )
  146.         FatalError( "Couldn't open the log file: %s\n", crc_file_name );
  147.     for ( ; ; ) {
  148.         result = fscanf( crc_file, "%lx %s", &log_crc, log_name );
  149.         if ( result < 2 )
  150.             break;
  151.         test_file = fopen( log_name, "rb" );
  152.         if ( test_file != NULL ) {
  153.             printf( "Checking %s ", log_name  );
  154.             crc = CalculateFileCRC( test_file );
  155.             fclose( test_file );
  156.             if ( crc != log_crc )
  157.                 printf( "Error:  Expected %08lx, got %08lx\n",
  158.                         log_name, log_crc, crc );
  159.             else
  160.                 printf( "OK\n" );
  161.         } else
  162.             printf( "Could not open file %s\n", log_name );
  163.     }
  164. }
  165. /* ProcessAllFiles() scans directory. Routine also makes sure that directory 
  166.  * name passed on command line is stripped of trailing '/' or '\' character. */
  167. void BuildCRCFile( input_dir_name, crc_file_name )
  168. char *input_dir_name;
  169. char *crc_file_name;
  170. {
  171.     char path[ FILENAME_SIZE ];
  172.     FILE *crc_file;
  173.  
  174.     strcpy( path, input_dir_name );
  175.     if ( path[ strlen( path ) - 1 ] == SEPARATOR[ 0 ] )
  176.         path[ strlen( path ) - 1 ] = '\0';
  177.     crc_file = fopen( crc_file_name, "w" );
  178.     if ( crc_file == NULL )
  179.         FatalError( "Can't open crc log file: %s\n", crc_file_name );
  180.     ProcessAllFiles( path, crc_file );
  181. }
  182. /* This routine is responsible for actually performing the calculation of the 
  183.  * 32-bit CRC for the entire file. We precondition the CRC value with all 1's,
  184.  * then invert every bit after the entire file has been done. This gives us a
  185.  * CRC value that corresponds with the values calculated by PKZIP and ARJ. */
  186. unsigned long CalculateFileCRC( file )
  187. FILE *file;
  188. {
  189.     unsigned long crc;
  190.     int count;
  191.     unsigned char buffer[ 512 ];
  192.     int i;
  193.  
  194.     crc = 0xFFFFFFFFL;
  195.     i = 0;
  196.     for ( ; ; ) {
  197.         count = fread( buffer, 1, 512, file );
  198.         if ( ( i++ % 32 ) == 0 )
  199.             putc( '.', stdout );
  200.         if ( count == 0 )
  201.             break;
  202.         crc = CalculateBufferCRC( count, crc, buffer );
  203.     }
  204.     putc( ' ', stdout );
  205.     return( crc ^= 0xFFFFFFFFL );
  206. }
  207. /* This is the routine that is responsible for calculating all of CRC values 
  208.  * for files in a given directory. The CRC values and file names are written 
  209.  * out to the crc_file. */
  210. void ProcessAllFiles( path, crc_file )
  211. char *path;
  212. FILE *crc_file;
  213. {
  214. #ifdef UNIX
  215.     DIR *dirp;
  216. #ifdef M_XENIX
  217.     struct direct *entry;
  218. #else
  219.     struct dirent *entry;
  220. #endif /* M_XENIX */
  221. #define NAME entry->d_name
  222. #else
  223.     FILE_INFO fileinfo;
  224.     int done;
  225. #define NAME FILE_NAME( fileinfo )
  226. #endif
  227.     char fullname[ FILENAME_SIZE ];
  228.     struct stat buf;
  229.     unsigned long crc;
  230.     FILE *file;
  231.  
  232.     printf( "Searching %s\n", path );
  233.     strcat( path, SEPARATOR );
  234. #ifdef UNIX
  235.     dirp = opendir( path );
  236.     if ( dirp == NULL )
  237.         FatalError( "Error opening directory %s\n", path );
  238.     entry = readdir( dirp );
  239.     while ( entry != 0 ) {
  240. #else
  241.     strcpy( fullname, path );
  242.     strcat( fullname, "*.*" );
  243.     done = FIND_FIRST( fullname, &fileinfo );
  244.     while ( done == 0 ) {
  245. #endif
  246.         strcpy( fullname, path );
  247.         if ( strcmp( NAME, "." ) && strcmp( NAME, ".." ) ) {
  248.             strcat( fullname, NAME );
  249.             if ( stat( fullname, &buf ) == -1 )
  250.                 FatalError( "Error reading stat from file %s!\n", fullname );
  251.             if ( buf.st_mode & S_IFDIR )
  252.                 ProcessAllFiles( fullname, crc_file );
  253.             else {
  254.                 file = fopen( fullname, "rb" );
  255.                 if ( file != NULL ) {
  256.                     printf( "Scanning %s ", fullname  );
  257.                     crc = CalculateFileCRC( file );
  258.                     putc( '\n', stdout );
  259.                     fprintf( crc_file, "%08lx %s\n", crc, fullname );
  260.                     fclose( file );
  261.                } else
  262.                     printf( "Could not open %s!\n", fullname );
  263.             }
  264.         }
  265. #ifdef UNIX
  266.         entry = readdir( dirp );
  267. #else
  268.         done = FIND_NEXT( &fileinfo );
  269. #endif
  270.     }
  271. }
  272. /* Routine calculates the CRC for a block of data using table lookup method. 
  273.  * It accepts an original value for the crc, and returns the updated value. */
  274. unsigned long CalculateBufferCRC( count, crc, buffer )
  275. unsigned int count;
  276. unsigned long crc;
  277. void *buffer;
  278. {
  279.     unsigned char *p;
  280.     unsigned long temp1;
  281.     unsigned long temp2;
  282.  
  283.     p = (unsigned char*) buffer;
  284.     while ( count-- != 0 ) {
  285.         temp1 = ( crc >> 8 ) & 0x00FFFFFFL;
  286.         temp2 = CRCTable[ ( (int) crc ^ *p++ ) & 0xff ];
  287.         crc = temp1 ^ temp2;
  288.     }
  289.     return( crc );
  290. }
  291. /* Fatal error handler prints a formatted error message and then exits. */
  292. #ifdef UNIX
  293.  
  294. void FatalError( va_alist )
  295. va_dcl
  296. {
  297.     char *fmt;
  298.     va_list argptr;
  299.  
  300.     va_start( argptr );
  301.     fmt = va_arg( argptr, char * );
  302. #else
  303.  
  304. void FatalError( char *fmt, ... )
  305. {
  306.     va_list argptr;
  307.     va_start( argptr, fmt );
  308. #endif
  309.  
  310.     printf( "Fatal error: " );
  311.     vprintf( fmt, argptr );
  312.     va_end( argptr );
  313.     exit( -1 );
  314. }
  315.  
  316.